package com.my.study.common.config;

import com.my.study.common.security.*;
import com.my.study.common.security.account.AccountAuthenticationProcessingFilter;
import com.my.study.common.security.account.AccountAuthenticationProvider;
import com.my.study.common.security.gitee.GiteeOAuth2UserService;
import com.my.study.common.security.remember.CustomRedisPersistentTokenRepository;
import com.my.study.dto.JwtUserDto;
import com.my.study.service.impl.UserDetailServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.web.authentication.RememberMeServices;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

/**
 * web security配置类
 *
 * @author yidujun
 * @date 2022/8/3 14:21
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Resource(name = "customAuthenticationSuccessHandler")
    private CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler;

    @Resource(name = "customAuthenticationFailureHandler")
    private CustomAuthenticationFailureHandler customAuthenticationFailureHandler;

    @Resource(name = "customAuthenticationEntryPoint")
    private CustomAuthenticationEntryPoint customAuthenticationEntryPoint;

    @Resource(name = "customAccessDeniedHandler")
    private CustomAccessDeniedHandler customAccessDeniedHandler;

    @Resource(name = "customLogoutSuccessHandler")
    private CustomLogoutSuccessHandler customLogoutSuccessHandler;

    @Resource(name = "accountAuthenticationProvider")
    private AccountAuthenticationProvider accountAuthenticationProvider;

    @Resource(name = "userDetailServiceImpl")
    private UserDetailServiceImpl userDetailServiceImpl;

    @Resource(name = "customRedisPersistentTokenRepository")
    private CustomRedisPersistentTokenRepository customRedisPersistentTokenRepository;
    
    @Autowired
    private DataSource dataSource;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(accountAuthenticationProvider);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and().csrf().disable();

        http.addFilterBefore(accountAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
                .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);

        http.oauth2Login()
                .successHandler(customAuthenticationSuccessHandler)
                .failureHandler(customAuthenticationFailureHandler)
        .userInfoEndpoint().customUserType(JwtUserDto.class, "gitee")
        .userService(giteeOAuth2UserService())
        ;
        
        //.loginProcessingUrl("/login/oauth2/code/gitee")
        //.authorizationEndpoint().baseUri("/oauth2/authorization")
        //.redirectionEndpoint().baseUri("/").and().userInfoEndpoint().customUserType()
        ;

//        http.oauth2Client();

        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                // 登录路径，无需认证
                .authorizeRequests()
                .antMatchers("/account/login", "/phone/login", "/**/register", "/**/getUser", "/api/callback", "/ok")
                .permitAll()
                .mvcMatchers("").hasRole("")
                // 其它地址需要认证
                .anyRequest().authenticated().and()
                // 登出
                .logout()
                .logoutUrl("/logout")
                .permitAll().logoutSuccessHandler(customLogoutSuccessHandler).and()
                // 未登录访问或无权限访问统一处理
                .exceptionHandling().authenticationEntryPoint(customAuthenticationEntryPoint)
                .accessDeniedHandler(customAccessDeniedHandler).and()
                // 记住我
                .rememberMe()
                .key("INTERNAL_SECRET_KEY")
                .rememberMeServices(rememberMeServices())
                ;
        
        
        
    }

    @Bean(name = "jwtAuthenticationFilter")
    public JwtAuthenticationFilter jwtAuthenticationFilter() throws Exception {
        return new JwtAuthenticationFilter(super.authenticationManager());
    }

    @Bean(name = "accountAuthenticationProcessingFilter")
    public AccountAuthenticationProcessingFilter accountAuthenticationProcessingFilter() throws Exception {
        AccountAuthenticationProcessingFilter filter = new AccountAuthenticationProcessingFilter();
        filter.setAuthenticationManager(super.authenticationManager());
        filter.setRememberMeServices(rememberMeServices());

        filter.setAuthenticationSuccessHandler(customAuthenticationSuccessHandler);
        filter.setAuthenticationFailureHandler(customAuthenticationFailureHandler);
        return filter;
    }

    @Bean(name = "passwordEncoder")
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean(name = "rememberMeServices")
    public RememberMeServices rememberMeServices() {
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);

        return new PersistentTokenBasedRememberMeServices(
                "INTERNAL_SECRET_KEY",
                userDetailServiceImpl,
                jdbcTokenRepository);
    }
    
    @Bean(name = "giteeOAuth2UserService")
    public GiteeOAuth2UserService giteeOAuth2UserService() {
        Map<String, Class<? extends OAuth2User>> customUserTypes = new HashMap<>();
        customUserTypes.put("gitee", JwtUserDto.class);
        return new GiteeOAuth2UserService(customUserTypes);
    }

}
